#include "LecteurPhraseAvecArbre.h"

#include <stdlib.h>
#include <iostream>

#include "ErreurInterpreteur.h"
using namespace std;

////////////////////////////////////////////////////////////////////////////////
LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
	ls(nomFich), ts() {
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::analyse() {
	arbre=programme();
	cout << "Syntaxe correcte." << endl;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::programme() {
// <programme> ::= debut <seqInst> fin FIN_FICHIER
	
	sauterSymCour("debut");
	Noeud* si = seqInst();
	sauterSymCour("fin");
	testerSymCour("<FINDEFICHIER>");
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::seqInst() {
// <seqInst> ::= <inst> ; { <inst> ; }
	
	NoeudSeqInst* si = new NoeudSeqInst();
	do {
		si->ajouteInstruction(inst());
		sauterSymCour(";");
	} while (this->isVariable() || this->isInstSi() || this->isInstSelon() ||
			this->isInstTq() || this->isInstRepeter() || this->isInstPour() ||
			this->isInstLire() || this->isInstEcrire());
	// tant que le symbole courant est un debut possible d'instruction...
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::inst() {
// <inst> ::= <affectation> | <instSi> | <instSelon> | <instTq> |
//		<instRepeter> | <instPour> | <instLire> | <instEcrire>

	Noeud* inst = NULL;
	
	if(this->isVariable()){
		inst = affectation();
	}
	else if(this->isInstSi()){
		inst = instSi();
	}
	else if(this->isInstSelon()){
		inst=instSelon();
	}
	else if(this->isInstTq()){
		inst = instTq();
	}
	else if(this->isInstRepeter()){
		inst = instRepeter();
	}
	else if(this->isInstPour()){
		inst = instPour();
	}
	else if(this->isInstLire()){
		inst = instLire();
	}
	else if(this->isInstEcrire()){
		inst = instEcrire();
	}
	else{
		erreur("<inst>");
	}
	return inst;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::affectation() {
// <affectation> ::= <variable> = <expression>

	testerSymCour("<VARIABLE>");
	Noeud* var = ts.chercheAjoute(ls.getSymCour());
	ls.suivant();
	sauterSymCour("=");
	Noeud* exp = expression();
	return new NoeudAffectation(var,exp);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expression() {
// <expression> ::= <terme> { <opAdd> <terme> }

	Noeud* ter = terme();
	while (this->isOpAdd()){
		Symbole oper = opAdd();
		Noeud* terDroit = terme();
		ter = new NoeudOperateurBinaire(oper, ter, terDroit);
	}
	return ter;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::terme(){
// <terme> ::= <facteur> {<opMult> <facteur>}
	
	Noeud* fact = facteur();
	while (this->isOpMult()){
		Symbole oper = opMult();
		Noeud* factDroit = facteur();
		fact = new NoeudOperateurBinaire(oper, fact, factDroit);
	}
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::facteur() {
// <facteur> ::= <entier> | <variable> | <chaine> | <opUnaire> <expBool> | ( <expBool> )

	Noeud* fact = NULL;
	if (this->isVariable()
			|| this->isEntier()
			|| this->isChaine())
	{
		fact=ts.chercheAjoute(ls.getSymCour());
		ls.suivant();
	}
	else if (this->isOpUnaire()) {
		Symbole oper;
		oper = opUnaire();
		fact = new NoeudOperateurUnaire(oper, expBool());
	}
	else if (ls.getSymCour()=="(") {
		ls.suivant();
		fact=expBool();
		sauterSymCour(")");
	}
	else
	{
		erreur("<facteur>");
	}
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expBool() {
// <expBool> ::= <termBool> { <opOu> <termBool> }
	
	Noeud* terB = termBool();
	while(this->isOpOu()){
		Symbole oper = opOu();
		terB = new NoeudOperateurBinaire(oper, terB, termBool());
	}
	return terB;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::termBool() {
// <termBool> ::= <relation> { <opEt> <relation> }
	
	Noeud* rel = relation();
	while(this->isOpEt()){
		Symbole oper = opEt();
		rel = new NoeudOperateurBinaire(oper, rel, relation());
	}
	return rel;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::relation(){
// <relation> ::= <expression> { <opRel> <expression> }
	Noeud* exp = expression();
	while(this->isOpRel()){
		Symbole oper = opRel();
		exp = new NoeudOperateurBinaire(oper, exp, expression());
	}
	return exp;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opEt(){
// <opEt> ::= et
	
	Symbole operateur;
	if (this->isOpEt()){
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else{
		erreur("<opEt>");
	}
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opOu(){
// <opOu> ::= ou
	
	Symbole operateur;
	if (this->isOpOu()){
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else{
		erreur("<opOu>");
	}
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opRel(){
// <opRel> ::= == | != | < | <= | > | >=
	
	Symbole operateur;	
	if (this->isOpRel()){
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else{
		erreur("<opRel>");
	}
	return operateur;
}


////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opAdd(){
// <opAdd> ::= + | -

	Symbole operateur;
	if (this->isOpAdd()){
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else{
		erreur("<opAdd>");
	}
	return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opMult(){
// <opMult> ::= * | /
	
	Symbole operateur;
	if(this->isOpMult()){
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else{
		erreur("<opMult>");
	}
	return operateur;
}

Symbole LecteurPhraseAvecArbre::opUnaire(){
// <opUnaire> ::= - | non
	
	Symbole operateur;
	if(this->isOpUnaire()){
		operateur=ls.getSymCour();
		ls.suivant();
	}
	else{
		erreur("<opMult>");
	}
	return operateur;
}


////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instSi(){
// <instSi> ::= si ( <expBool> ) <seqInst> { sinonsi ( <expBool> ) <seqInst> } [ sinon <seqInst> ] finsi

	NoeudInstSi* nsi = new NoeudInstSi();
	
	if(this->isInstSi()){
		ls.suivant();
		sauterSymCour("(");
		Noeud* condsi = expBool();
		sauterSymCour(")");
		Noeud * seqsi = seqInst();
		nsi->ajoute(condsi, seqsi);
		
		while(ls.getSymCour()=="sinonsi"){
			ls.suivant();
			sauterSymCour("(");
			Noeud* cond = expBool();
			sauterSymCour(")");
			Noeud * seq = seqInst();
			nsi->ajoute(cond, seq);
		}
		
		if(ls.getSymCour()=="sinon"){
			ls.suivant();
			nsi->ajoute(NULL, seqInst());
		}
		sauterSymCour("finsi");
	}
	else{
		erreur("<instSi>");
	}
	return nsi;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instSelon(){
//	 <instSelon> ::= selon ( <variable> ) cas <expression> : <seqInst>
//			{ cas <expression> : <seqInst> } [ defaut : <seqInst> ] finselon

	NoeudInstSelon* ns = NULL;
	
	if(this->isInstSelon()){
		sauterSymCour("selon");
		sauterSymCour("(");
		testerSymCour("<VARIABLE>");
		ns = new NoeudInstSelon(ts.chercheAjoute(ls.getSymCour()));
		ls.suivant();
		sauterSymCour(")");
		do{
			sauterSymCour("cas");
			Noeud* cond = expression();
			sauterSymCour(":");
			Noeud* seq = seqInst();
			ns->ajoute(cond, seq);
		} while (ls.getSymCour()=="cas");
		if(ls.getSymCour()=="defaut"){
			ls.suivant(),
			sauterSymCour(":");
			ns->ajoute(NULL, seqInst());
		}
		sauterSymCour("finselon");
	}
	else{
		erreur("<instSelon>");
	}
	
	return ns;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instTq(){
// <instTq> ::= tantque ( <expBool> ) <seqInst> fintantque
	
	Noeud* ntq = NULL;

	if(this->isInstTq()){
		ls.suivant();
		sauterSymCour("(");
		Noeud* exp = expBool();
		sauterSymCour(")");
		Noeud* seq = seqInst();
		sauterSymCour("fintantque");
		ntq = new NoeudInstTq(exp, seq);
	}
	else{
		erreur("<instTq>");
	}
	return ntq;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instRepeter(){
// <instRepeter> ::= repeter <seqInst> jusqua ( <expBool> )
	
	Noeud* nrpt = NULL;
	if(this->isInstRepeter()){
		ls.suivant();
		Noeud* seq = seqInst();
		sauterSymCour("jusqua");
		sauterSymCour("(");
		Noeud* exp = expBool();
		sauterSymCour(")");
		nrpt = new NoeudInstRepeter(seq, exp);
	}
	else{
		erreur("<instRepeter>");
	}
	return nrpt;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instPour(){
// <instPour> ::= pour ( <affectation> ; <expBool> ; <inst> ) <seqInst> finpour
	
	Noeud* npr = NULL;
	if (this->isInstPour()){
		sauterSymCour("pour");
		sauterSymCour("(");
		Noeud* aff = affectation();
		sauterSymCour(";");
		Noeud* exp = expBool();
		sauterSymCour(";");
		Noeud* instr = inst();
		sauterSymCour(")");
		Noeud* seq = seqInst();
		sauterSymCour("finpour");
		npr = new NoeudInstPour(aff, exp, instr, seq);
	}
	else
	{
		erreur("<instPour>");
	}
	return npr;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instLire(){
// <instLire> ::= lire ( <variable> )

	Noeud* nlire = NULL;
	if(this->isInstLire()){
		sauterSymCour("lire");
		sauterSymCour("(");
		testerSymCour("<VARIABLE>");
		nlire = new NoeudInstLire(ts.chercheAjoute(ls.getSymCour()));
		ls.suivant();
		sauterSymCour(")");
	}
	else{
		erreur("<instLire>");
	}
	return nlire;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instEcrire(){
// <instEcrire> ::= ecrire ( <expression> )
	
	Noeud* necrire = NULL;
	if(this->isInstEcrire()){
		sauterSymCour("ecrire");
		sauterSymCour("(");
		necrire = new NoeudInstEcrire(expression());
		sauterSymCour(")");
	}
	else{
		erreur("<instEcrire>");
	}
	return necrire;
}


////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::testerSymCour(string ch) {
	if (ls.getSymCour() != ch) {
		cout << endl << "-------- Erreur ligne " << ls.getLigne()
				<< " - Colonne " << ls.getColonne() << endl << "   Attendu : "
				<< ch << endl << "   Trouve  : " << ls.getSymCour() << endl
				<< endl;
		throw ErreurTestSymbole(); // levée d'exception
	}
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
	testerSymCour(ch);
	ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::erreur(string mess) {
	cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
			<< ls.getColonne() << endl << "   Attendu : " << mess << endl
			<< "   Trouve  : " << ls.getSymCour() << endl << endl;
	throw ErreurStandard(); // levée d'exception
}
